<?php


namespace SFExpressIsp;


use Exception;
use phpseclib\Crypt\AES;
use Ramsey\Uuid\Uuid;
use SFExpressIsp\Core\ServiceInterface;

class Client
{
    /**
     * @var ServiceInterface
     * @author luffyzhao@vip.126.com
     */
    private $service;
    /**
     * @var string 客户编码
     * @author luffyzhao@vip.126.com
     */
    protected $partnerID;
    /**
     * @var string 接入代码
     * @author luffyzhao@vip.126.com
     */
    protected $partnerToken;
    /**
     * @var string 版本号
     * @author luffyzhao@vip.126.com
     */
    protected $versionID = 'v2.1.4';
    /**
     * @var string 接口报文类型
     * @author luffyzhao@vip.126.com
     */
    protected $documentType;
    /**
     * @var string 报文发送方代码
     * @author luffyzhao@vip.126.com
     */
    protected $senderID;
    /**
     * @var string 报文接收方代码
     * @author luffyzhao@vip.126.com
     */
    protected $receiverID;

    /**
     * @var string 请求唯一号
     * @author luffyzhao@vip.126.com
     */
    protected $requestID;
    /**
     * @var string  语言
     * @author luffyzhao@vip.126.com
     */
    protected $language = 'zh-CN';
    /**
     * @var string 时区
     * @author luffyzhao@vip.126.com
     */
    protected $timeZone = '+08:00';

    /**
     * @var string AES加密秘钥
     * @author luffyzhao@vip.126.com
     */
    protected $aesKey = '';

    /**
     * @var string HMAC-SHA512完整性秘钥
     * @author luffyzhao@vip.126.com
     */
    protected $shaKey = '';

    /**
     * @var string 通用的接口服务代码
     * @author luffyzhao@vip.126.com
     */
    protected $serviceCode = '';

    /**
     * @var string
     * @author luffyzhao@vip.126.com
     */
    protected $uri = '';


    public function __construct(ServiceInterface $service)
    {
        $this->service = $service;
    }

    /**
     * @param string $requestID
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setRequestID(string $requestID): Client
    {
        $this->requestID = $requestID;
        return $this;
    }

    /**
     * @param string $uri
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setUri(string $uri): Client
    {
        $this->uri = $uri;
        return $this;
    }

    /**
     * @param string $partnerID
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setPartnerID(string $partnerID): Client
    {
        $this->partnerID = $partnerID;
        return $this;
    }

    /**
     * @param string $partnerToken
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setPartnerToken(string $partnerToken): Client
    {
        $this->partnerToken = $partnerToken;
        return $this;
    }

    /**
     * @param string $versionID
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setVersionID(string $versionID): Client
    {
        $this->versionID = $versionID;
        return $this;
    }

    /**
     * @param string $documentType
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setDocumentType(string $documentType): Client
    {
        $this->documentType = $documentType;
        return $this;
    }

    /**
     * @param string $senderID
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setSenderID(string $senderID): Client
    {
        $this->senderID = $senderID;
        return $this;
    }

    /**
     * @param string $receiverID
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setReceiverID(string $receiverID): Client
    {
        $this->receiverID = $receiverID;
        return $this;
    }

    /**
     * @param string $language
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setLanguage(string $language): Client
    {
        $this->language = $language;
        return $this;
    }

    /**
     * @param string $timeZone
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setTimeZone(string $timeZone): Client
    {
        $this->timeZone = $timeZone;
        return $this;
    }
    /**
     * @param string $aesKey
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setAesKey(string $aesKey): Client
    {
        $this->aesKey = $aesKey;
        return $this;
    }

    /**
     * @param string $serviceCode
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setServiceCode(string $serviceCode): Client
    {
        $this->serviceCode = $serviceCode;
        return $this;
    }

    /**
     * @param string $shaKey
     * @return Client
     * @author luffyzhao@vip.126.com
     */
    public function setShaKey(string $shaKey): Client
    {
        $this->shaKey = $shaKey;
        return $this;
    }

    /**
     * @return string
     * @author luffyzhao@vip.126.com
     */
    public function getXmlMsg(): string
    {
        return '<?xml version="1.0" encoding="UTF-8"?><Request>' .
            '<Header>' .
            '<PartnerID>' . $this->partnerID . '</PartnerID>' .
            '<PartnerToken>' . $this->partnerToken . '</PartnerToken>' .
            '<VersionID>' . $this->versionID . '</VersionID>' .
            '<DocumentType>' . $this->documentType . '</DocumentType>' .
            '<SenderID>' . $this->senderID . '</SenderID>' .
            '<ReceiverID>' . $this->receiverID . '</ReceiverID>' .
            '<Timestamp>' . date('Y-m-d H:i:s') . '</Timestamp>' .
            '<RequestID>' . $this->requestID . '</RequestID>' .
            '<Language>' . $this->language . '</Language>' .
            '<TimeZone>' . $this->timeZone . '</TimeZone>' .
            '</Header>' .
            '<Body>' . $this->service->__toString() . '</Body>' .
            '</Request>';
    }

    /**
     * @return string
     * @author luffyzhao@vip.126.com
     */
    protected function getMsgData(): string
    {
        $aes = new AES();
        $aes->setKey($this->aesKey);
        return base64_encode($aes->encrypt($this->getXmlMsg()));
    }

    /**
     * @author luffyzhao@vip.126.com
     */
    protected function getPartnerID()
    {
        return $this->partnerID;
    }

    /**
     * @author luffyzhao@vip.126.com
     */
    protected function getServiceCode()
    {
        return $this->serviceCode;
    }

    /**
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @author luffyzhao@vip.126.com
     */
    public function request(){
        $http = new \GuzzleHttp\Client();
        $res = $http->request(
            'post',
            $this->uri,
            [
                'headers' => [
                    'Content-type' => 'application/xml；charset=UTF-8'
                ],
                'body' => $this->__toString()
            ]
        );
        if($res->getStatusCode() !== 200){
            throw new Exception('失败');
        }
        $resArr = $this->xml2Arr($res->getBody()->getContents());
        if($resArr['Head'] !== 'OK'){
            throw new Exception($resArr['Error'], $resArr['Code']);
        }
        return $resArr['Body']['OrderResponse'];
    }

    /**
     * @param $xmlStr
     * @return mixed
     * @author luffyzhao@vip.126.com
     */
    protected function xml2Arr($xmlStr){
        $obj = simplexml_load_string($xmlStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        return json_decode(json_encode($obj),true);
    }
    /**
     * @return string
     * @author luffyzhao@vip.126.com
     */
    public function __toString() : string{
        $msgData = $this->getMsgData();
        $dataDigest = hash_hmac('sha512', $msgData, base64_encode($this->shaKey));
        return '<?xml version="1.0" encoding="UTF-8"?>' .
            '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' .
                '<soap:Body>' .
                    '<tns:sfexpressService xmlns:tns="http://service.expressservice.integration.sf.com/">' .
                        '<MsgData>'.$msgData.'</MsgData>' .
                        '<DataDigest>'.$dataDigest.'</DataDigest>' .
                        '<PartnerID>'.$this->partnerID.'</PartnerID>' .
                        '<ServiceCode>'.$this->serviceCode.'</ServiceCode>' .
                    '</tns:sfexpressService>' .
                '</soap:Body>' .
            '</soap:Envelope>';
    }

}